package com.boom.monitor.service;

import com.boom.common.utils.StringUtils;
import com.boom.monitor.VO.jmx.ThreadInfo;
import com.boom.monitor.VO.jmx.*;
import com.boom.monitor.config.JmxConnectorInstance;
import com.boom.monitor.util.EntryUtils;
import com.boom.monitor.util.JmxUtils;
import org.springframework.stereotype.Service;

import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.lang.management.*;
import java.util.*;

import static com.boom.monitor.util.FormatterUnitUtils.firstCharCase;
import static com.boom.monitor.util.FormatterUnitUtils.formatterUnit;

/**
 * @author:yzw
 * @Date:2021/3/1 上午10:15
 * @desc:
 * @version:1.0
 */
@Service
public class BoomJmxService {

	private static final String HEAP_MEMORY = "HeapMemoryUsage";

	private static final String NON_HEAP_MEMORY = "NonHeapMemoryUsage";

	public static void main(String[] args) {
		try {
			Overview overview = overview("127.0.0.1", 8999);
			System.out.println(overview);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 总览
	 *
	 * @param ip
	 * @param port
	 * @return com.xzops.bnm.service.xzmonitor.model.jmx.Overview
	 * @author yzw
	 * @time 2021/3/1 下午4:34
	 */
	public static Overview overview(String ip, Integer port) throws IOException {
		if (StringUtils.isNotEmpty(ip) && port != null) {
			setConnection(ip, port);
		}
		OperatingSystemMXBean operatingSystemMxBean = ManagementFactory.getOperatingSystemMXBean();
		RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();
		ObjectName systemBeanObjectName = operatingSystemMxBean.getObjectName();
		BeanInfo systemBean = JmxUtils.getObjectNameProperties(systemBeanObjectName.getCanonicalName());
		BeanInfo jvmBean = JmxUtils.getObjectNameProperties(runtimeMxBean.getObjectName().getCanonicalName());

		Overview overview = new Overview();
		SystemInfo systemInfo = buildSystemInfo(systemBean);
		JvmInfo jvmInfo = buildJvmInfo(jvmBean);
		overview.setSystemInfo(systemInfo);
		overview.setJvmInfo(jvmInfo);
		buildMemoryInfo(overview);
		ThreadInfo threadInfo = buildThreadInfo();
		overview.setThreadInfo(threadInfo);

		ClassLoadingInfo classLoadingInfo = buildClassInfo();
		overview.setClassLoadingInfo(classLoadingInfo);
		try {
			List<GarbageInfo> garbageInfos = buildGarbageCollectorInfo();
			overview.setGarbageCollectorInfo(garbageInfos);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return overview;
	}

	/**
	 * @param ip
	 * @param port
	 * @return void
	 * @desc 设置连接
	 * @author yzw
	 * @time 2021/3/1 下午5:16
	 */
	private static void setConnection(String ip, Integer port) throws IOException {
		String jmxUrl = String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", ip, port);
		JMXServiceURL jmxServiceUrl = new JMXServiceURL(jmxUrl);
		JMXConnector jmxConnector = JMXConnectorFactory.connect(jmxServiceUrl, null);
		JmxConnectorInstance.INSTANCE.setJmxConnector(jmxConnector);
	}

	/**
	 * 系统参数
	 *
	 * @param systemBean
	 * @return com.xzops.bnm.service.xzmonitor.model.jmx.SystemInfo
	 * @author yzw
	 * @time 2021/3/1 下午4:35
	 */
	private static SystemInfo buildSystemInfo(BeanInfo systemBean) {
		List<BeanAttributeInfo> attributeInfos = systemBean.getBeanAttributeInfos();
		SystemInfo systemInfo = new SystemInfo();
		for (BeanAttributeInfo beanAttributeInfo : attributeInfos) {
			EntryUtils.setValue(systemInfo, firstCharCase(beanAttributeInfo.getName()), beanAttributeInfo.getValue().getData());
		}
		return systemInfo;
	}

	/**
	 * @param runtimeBean
	 * @return com.xzops.bnm.service.xzmonitor.model.jmx.JvmInfo
	 * @desc jvm 参数
	 * @author yzw
	 * @time 2021/3/1 下午4:35
	 */
	@SuppressWarnings("unchecked")
	private static JvmInfo buildJvmInfo(BeanInfo runtimeBean) {
		List<BeanAttributeInfo> attributeInfos = runtimeBean.getBeanAttributeInfos();
		JvmInfo jvmInfo = new JvmInfo();
		for (BeanAttributeInfo beanAttributeInfo : attributeInfos) {
			String name = firstCharCase(beanAttributeInfo.getName());
			if (!"systemProperties".equals(name)) {
				EntryUtils.setValue(jvmInfo, name, beanAttributeInfo.getValue().getData());
			} else {
				// systemProperties 格式特殊 需要单独处理
				List<TreeMap<String, Object>> properties = (ArrayList<TreeMap<String, Object>>) beanAttributeInfo.getValue().getData();
				jvmInfo.setSystemProperties(properties);
			}
		}
		return jvmInfo;
	}

	/**
	 * @param overview
	 * @return void
	 * @desc jvm 内存信息 堆和非堆
	 * @author yzw
	 * @time 2021/3/1 下午4:35
	 */
	private static void buildMemoryInfo(Overview overview) {
		MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();
		BeanInfo beanInfo = JmxUtils.getObjectNameProperties(memoryMxBean.getObjectName().getCanonicalName());
		List<BeanAttributeInfo> beanAttributeInfos = beanInfo.getBeanAttributeInfos();
		for (BeanAttributeInfo beanAttributeInfo : beanAttributeInfos) {
			if (beanAttributeInfo.getName().equals(HEAP_MEMORY)) {
				overview.setHeapMemoryUsage(buildMemoryUsage(beanAttributeInfo));
			} else if (beanAttributeInfo.getName().equals(NON_HEAP_MEMORY)) {
				overview.setNonHeapMemoryUsage(buildMemoryUsage(beanAttributeInfo));
			}
		}
	}

	/**
	 * @param beanAttributeInfo
	 * @return java.lang.management.MemoryUsage
	 * @desc 内存信息
	 * @author yzw
	 * @time 2021/3/1 下午4:36
	 */
	private static XzMemoryUsage buildMemoryUsage(BeanAttributeInfo beanAttributeInfo) {
		@SuppressWarnings("unchecked")
		SortedMap<String, Object> map = (TreeMap<String, Object>) beanAttributeInfo.getValue().getData();
		String init = formatterUnit(Long.parseLong(map.get("init").toString()));
		String used = formatterUnit(Long.parseLong(map.get("used").toString()));
		String committed = formatterUnit(Long.parseLong(map.get("committed").toString()));
		String max = formatterUnit(Long.parseLong(map.get("max").toString()));
		return new XzMemoryUsage(init, used, committed, max);
	}

	/**
	 * @param
	 * @return com.xzops.bnm.service.xzmonitor.model.jmx.ThreadInfo
	 * @desc 线程信息
	 * @author yzw
	 * @time 2021/3/1 下午4:36
	 */
	private static ThreadInfo buildThreadInfo() {
		ThreadInfo threadInfo = new ThreadInfo();
		ThreadMXBean threadMxBean = ManagementFactory.getThreadMXBean();
		BeanInfo beanInfo = JmxUtils.getObjectNameProperties(threadMxBean.getObjectName().getCanonicalName());
		List<BeanAttributeInfo> beanAttributeInfos = beanInfo.getBeanAttributeInfos();
		for (BeanAttributeInfo beanAttributeInfo : beanAttributeInfos) {
			switch (beanAttributeInfo.getName()) {
				case "ThreadCount":
					threadInfo.setLiveThreadCount((int) beanAttributeInfo.getValue().getData());
					break;
				case "DaemonThreadCount":
					threadInfo.setDaemonThreadCount((int) beanAttributeInfo.getValue().getData());
					break;
				case "TotalStartedThreadCount":
					threadInfo.setTotalStartedThreadCount((long) beanAttributeInfo.getValue().getData());
					break;
				case "PeakThreadCount":
					threadInfo.setLivePeakThreadCount((int) beanAttributeInfo.getValue().getData());
					break;
				default:
					break;
			}
		}
		return threadInfo;
	}

	/**
	 * @param
	 * @return com.xzops.bnm.service.xzmonitor.model.jmx.ClassLoadingInfo
	 * @desc class 信息
	 * @author yzw
	 * @time 2021/3/1 下午4:36
	 */
	private static ClassLoadingInfo buildClassInfo() {
		ClassLoadingInfo classLoadingInfo = new ClassLoadingInfo();
		ClassLoadingMXBean classLoadingMxBean = ManagementFactory.getClassLoadingMXBean();
		BeanInfo beanInfo = JmxUtils.getObjectNameProperties(classLoadingMxBean.getObjectName().getCanonicalName());
		List<BeanAttributeInfo> beanAttributeInfos = beanInfo.getBeanAttributeInfos();
		for (BeanAttributeInfo beanAttributeInfo : beanAttributeInfos) {
			switch (beanAttributeInfo.getName()) {
				case "TotalLoadedClassCount":
					classLoadingInfo.setTotalLoadedClassCount((long) beanAttributeInfo.getValue().getData());
					break;
				case "LoadedClassCount":
					classLoadingInfo.setLoadedClassCount((int) beanAttributeInfo.getValue().getData());
					break;
				case "UnloadedClassCount":
					classLoadingInfo.setUnloadedClassCount((long) beanAttributeInfo.getValue().getData());
					break;
				default:
					break;
			}
		}
		return classLoadingInfo;
	}

	/**
	 * @param
	 * @return java.util.List<com.xzops.bnm.service.xzmonitor.model.jmx.GarbageInfo>
	 * @desc 垃圾收集器信息
	 * @author yzw
	 * @time 2021/3/1 下午4:36
	 */
	private static List<GarbageInfo> buildGarbageCollectorInfo() throws Exception {
		List<GarbageInfo> garbageInfos = new ArrayList<>();
		JmxConnectorInstance commonConfig = JmxConnectorInstance.INSTANCE;
		JMXConnector connector = commonConfig.getJmxConnector();
		ObjectName queryObjectName = new ObjectName("java.lang:name=*,type=GarbageCollector");
		MBeanServerConnection msc = connector.getMBeanServerConnection();
		Set<ObjectName> objectNames = msc.queryNames(queryObjectName, null);
		for (ObjectName objectName : objectNames) {
			BeanInfo beanInfo = JmxUtils.getObjectNameProperties(objectName.getCanonicalName());
			List<BeanAttributeInfo> beanAttributeInfos = beanInfo.getBeanAttributeInfos();
			GarbageInfo garbageInfo = new GarbageInfo();
			for (BeanAttributeInfo beanAttributeInfo : beanAttributeInfos) {
				switch (beanAttributeInfo.getName()) {
					case "Name":
						garbageInfo.setName(beanAttributeInfo.getValue().getData().toString());
						break;
					case "CollectionCount":
						garbageInfo.setCollectionCount((long) beanAttributeInfo.getValue().getData());
						break;
					case "MemoryPoolNames":
						String[] pools = (String[]) beanAttributeInfo.getValue().getData();
						garbageInfo.setMemoryPoolNames(pools);
						break;
					case "CollectionTime":
						garbageInfo.setCollectionTime(beanAttributeInfo.getValue().getData() + "毫秒");
					default:
						break;
				}
			}
			garbageInfos.add(garbageInfo);
		}
		return garbageInfos;
	}
}
